﻿using Architecture.CrossCutting.Common;
using Architecture.CrossCutting.Common.Logging;
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Threading.Tasks;
using System.Web;

namespace Architecture.CrossCutting.Web.WCF
{

    public class PipelineDiagnosticClient : IClientMessageInspector, IParameterInspector, IChannelInitializer, IEndpointBehavior
    {

        IGenericLogger<WcfClientLog> _logger;

        public PipelineDiagnosticClient(IGenericLogger<WcfClientLog> logger)
        {
            _logger = logger;
            this.Extension = new PipelineExtension<WcfClientLog>();
        }

        public PipelineExtension<WcfClientLog> Extension { get; set; }

        public Boolean IsFullLog { get; set; }
        public Boolean IsEnabled { get; set; }
        public Boolean LogBeforeStep { get; set; }

        public Boolean LogAfterStep { get; set; }

        public void Initialize(IClientChannel channel)
        {
            channel.Extensions.Add(this.Extension);
        }

        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) {}

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            clientRuntime.ChannelInitializers.Add(this);
            clientRuntime.ClientMessageInspectors.Add(this);
            foreach (ClientOperation op in clientRuntime.Operations)
            {
                op.ParameterInspectors.Add(this);
            }

        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }

        public void Validate(ServiceEndpoint endpoint) { }

        public void AfterReceiveReply(ref Message reply, object correlationState)
        {

            RequestReplyCorrelation<WcfClientLog> correlationRequest = (correlationState as RequestReplyCorrelation<WcfClientLog>);
            if (correlationRequest == null)
            {
                throw new NotImplementedException("Manage this case or Not which never shouldn't fire");
            }

            //Stop the watcher
            correlationRequest.Watcher.Stop();

            //Complete the request Data
            correlationRequest.LogDetail.ResponseReceived = DateTime.UtcNow;
            correlationRequest.LogDetail.ResponseLength = reply.ToString().Length;
            correlationRequest.LogDetail.Step = WcfClientLog.PipelineStep.AfterReceiveReply;
            correlationRequest.LogDetail.IsFault = reply.IsFault;
            correlationRequest.LogDetail.TransportDuration = correlationRequest.Watcher.ElapsedMilliseconds;

            if (this.IsFullLog || this.LogAfterStep)
            {
                Task.Run(() =>
                {
                    _logger.WriteInfo(String.Format("Pipeline Step 3 : AfterReceiveReply - Correlation Id '{0}'", 1), correlationRequest.LogDetail);
                }).ContinueWith(completed =>
                {
                    switch (completed.Status)
                    {
                        case TaskStatus.Faulted:
                            {
                                _logger.WriteError(String.Format("Pipeline Step 3 : AfterReceiveReply - Correlation Id '{0}'", 1),
                                      correlationRequest.LogDetail,
                                    completed.Exception.InnerException);
                                break;
                            }
                    }
                });
            }
        }


        public object BeforeSendRequest(ref Message request, IClientChannel channel)
        {

            WcfClientLog log = null;
            Boolean fullPipelineInpsection = true;
            var pipelineExtension = channel.Extensions.Find<PipelineExtension<WcfClientLog>>();

            if (pipelineExtension != null)
            {
                log = pipelineExtension.LogDetail;
            }

            if (log == null)
            {
                fullPipelineInpsection = false;
                log = new WcfClientLog();
                //Todo : retrieve correlation Id
                log.UpdateExecutionContext();
            }

            //Set properties
            log.Action = request.Headers.Action;
            log.TransportDuration = 0;
            log.IsFault = false;
            log.RequestLength = request.ToString().Length;
            log.RequestSended = DateTime.UtcNow;
            log.Step = WcfClientLog.PipelineStep.BeforeSendRequest;

            System.Diagnostics.Stopwatch logWatcher = new System.Diagnostics.Stopwatch();
            logWatcher.Start();

            if (this.IsFullLog ||this.LogBeforeStep)
            {
                System.Threading.Tasks.Task.Run(() =>
                {
                    _logger.WriteInfo(String.Format("Pipeline Step 3 : BeforeSendRequest - Correlation Id '{0}'", 1),
                                      log);
                }).ContinueWith(completed =>
                {
                    switch (completed.Status)
                    {
                        case TaskStatus.RanToCompletion:
                            {
                                logWatcher.Stop();
                                log.RequestLogDuration = logWatcher.ElapsedMilliseconds;
                                break;
                            }

                        case TaskStatus.Faulted:
                            {
                                _logger.WriteError(String.Format("Pipeline Step 3 : BeforeSendRequest - Correlation Id '{0}'", 1),
                                      log,
                                    completed.Exception.InnerException);
                                break;
                            }
                    }

                });
            }
            System.Diagnostics.Stopwatch watcher = new System.Diagnostics.Stopwatch();
            watcher.Start();

            return new RequestReplyCorrelation<WcfClientLog>()
            {
                Watcher = watcher,
                LogDetail = log
            };

        }


        public object BeforeCall(string operationName, object[] inputs)
        {

            WcfClientLog log = new WcfClientLog()
            {
                OperationName = operationName,
                TransportDuration = 0,
                IsFault = false,
                PipeLineStarted = DateTime.UtcNow,
                Step = WcfClientLog.PipelineStep.BeforeCall
            };

            System.Diagnostics.Stopwatch logWatcher = new System.Diagnostics.Stopwatch();
            logWatcher.Start();

            if (this.IsFullLog || this.LogBeforeStep)
            {

                System.Threading.Tasks.Task.Run(() =>
                {
                    _logger.WriteInfo(String.Format("Pipeline Step 1 : BeforeCall - Correlation Id '{0}'", 1),
                                      log);
                }).ContinueWith(completed =>
                {
                    switch (completed.Status)
                    {
                        case TaskStatus.RanToCompletion:
                            {
                                logWatcher.Stop();
                                log.RequestLogDuration = logWatcher.ElapsedMilliseconds;
                                break;
                            }

                        case TaskStatus.Faulted:
                            {
                                _logger.WriteError(String.Format("Pipeline Step 1 : BeforeCall - Correlation Id '{0}'", 1),
                                      log,
                                    completed.Exception.InnerException);
                                break;
                            }
                    }

                });
            }

            if (this.Extension != null)
            {
                this.Extension.LogDetail = log;
            }


            System.Diagnostics.Stopwatch watcher = new System.Diagnostics.Stopwatch();
            watcher.Start();

            return new RequestReplyCorrelation<WcfClientLog>()
            {
                Watcher = watcher,
                LogDetail = log
            };
        }

        public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
        {
            if (!this.IsEnabled) { return; }

            RequestReplyCorrelation<WcfClientLog> correlationRequest = (correlationState as RequestReplyCorrelation<WcfClientLog>);
            if (correlationRequest == null)
            {
                throw new NotImplementedException("Manage this case or Not which never shouldn't fire");
            }

            //Stop the watcher
            correlationRequest.Watcher.Stop();

            var log = correlationRequest.LogDetail;

            //Complete the request Data
            log.Step = WcfClientLog.PipelineStep.AfterCall;
            log.PipeLineEnded = DateTime.UtcNow;
            log.PipeLineDuration = correlationRequest.Watcher.ElapsedMilliseconds;
            log.SerializationDuration = log.PipeLineDuration - log.TransportDuration;


            Task.Run(() =>
            {
                _logger.WriteInfo(String.Format("Pipeline Step 1 : AfterCall - Correlation Id '{0}'", 1), correlationRequest.LogDetail);
            }).ContinueWith(completed =>
            {
                switch (completed.Status)
                {
                    case TaskStatus.Faulted:
                        {
                            try
                            { 
                            _logger.WriteError(String.Format("Pipeline Step 1 : AfterCall - Correlation Id '{0}'", 1),
                                    correlationRequest.LogDetail,
                                completed.Exception.InnerException);
                            }
                            catch(Exception ex)
                            {
                                System.Diagnostics.EventLog eventLog = new System.Diagnostics.EventLog("Application");
                                eventLog.Source = "PipelineDiagnosticClient";
                                eventLog.WriteEntry(ex.ToString());
                            }
                            break;
                        }
                }
            });
        }


    }
}
